;翻页菜单简洁版
;FlameCyclone
;2024.5.5

;======================================================================
;配置与常量
    .INCLUDE "fc_menu_constant.asm"
    .INCLUDE "fc_menu_config.asm"

	.ORG $0000
	.BASE $00
	.HEX 4E 45 53 1A
	.DB NES_16KB_PRG_SIZE, NES_8KB_CHR_SIZE
	.DB ((Mapper & $F) << 4) | Trainer | FourScreen | Battery_backed | Mirroring
	.DB (Mapper & $F)

;======================================================================
;自定义数据
    .INCLUDE "fc_menu_theme.asm"

;----------------------------------------
;文本数据
    .INCLUDE "fc_game_list.asm"
    .INCLUDE "fc_game_text.asm"

;======================================================================
;节目信息数据
    BANK_ORG_8K CUSTOM_DATA_BANK & BANK_DATA_MASK, CUSTOM_DATA_ADDR
    .INCLUDE "build/game_final_data_0.asm"
    
;======================================================================
;主程序数据
    BANK_ORG_8K RESET_BANK & BANK_DATA_MASK, RESET_ADDR

    .INCLUDE "fc_menu_ppu.asm"
    .INCLUDE "fc_menu_joysitck.asm"
    .INCLUDE "fc_menu_text.asm"
    .INCLUDE "fc_menu_page.asm"
    .INCLUDE "fc_menu_music.asm"
    .INCLUDE "fc_menu_cursor.asm"
    .INCLUDE "fc_menu_effects.asm"

;======================================================================
;调色板清理
Palette_Clear
    LDA #$3F
    STA PPU_ADDRESS
    LDA #$00
    STA PPU_ADDRESS
    LDX #$20
    LDA #$0F
.Write_Data
    STA PPU_DATA
    DEX
    BNE .Write_Data
.End
    RTS

;======================================================================
;调色板初始化
Palette_Init
    LDA #$3F
    STA PPU_ADDRESS
    LDA #$00
    STA PPU_ADDRESS
    LDX #$00
    LDY #$20
Palette_Init.Write_Data
    LDA Palette_Data,X
    STA FC_PPU_Pal_Addr,X
    ;STA PPU_DATA
    INX
    DEY
    BNE Palette_Init.Write_Data
Palette_Init.End
    RTS

;======================================================================
;属性表初始化
Attributes_Init
    LDA #$23
    STA PPU_ADDRESS
    LDA #$C0
    STA PPU_ADDRESS
    LDX #$00
    LDY #$40
Attributes_Init.Write_Data
    LDA Attributes_Data,X
    STA PPU_DATA
    INX
    DEY
    BNE Attributes_Init.Write_Data
Attributes_Init.End
    RTS

;======================================================================
;命令表初始化
Nametable_Init
    LDA #$20
    STA PPU_ADDRESS
    LDA #$00
    STA PPU_ADDRESS
    LDA #<Nametable_Data
    STA FC_Data_Addr_L
    LDA #>Nametable_Data
    STA FC_Data_Addr_H
    LDY #$00
    LDX #$04
Nametable_Init.Write_Data
    LDA (FC_Data_Addr_L),Y
    STA PPU_DATA
    INY
    BNE Nametable_Init.Write_Data
    INC FC_Data_Addr_H
    DEX
    BNE Nametable_Init.Write_Data
Nametable_Init.End
    RTS

;======================================================================
;图案内存初始化
VRAM_Init
    .IF 0 == NES_8KB_CHR_SIZE
        LDA FC_Bank_Bak_A000
        PHA
        
        LDA #CHR_DATA_BANK & BANK_DATA_MASK
        STA FC_Data_Temp
        LDA #$00
        STA FC_Data_Count

;拷贝 CHR 数据 到 CHR-RAM
VRAM_Init.Write_CHR_To_Vram
        LDA FC_Data_Temp
        JSR Bank_Switch_A000
        
        LDA FC_Data_Count
        ASL
        ASL
        ASL
        TAY
VRAM_Init.Mapping_Vram_0000_0FFF;设置PPU地址 $0000-$07FF, MMC3 8000写入$00,$01
        LDX #$00
        STX MAPPER_REG_BANK_CTRL
        STY MAPPER_REG_BANK_DATA ;0000-07FF
        INX
        INY
        INY
        STX MAPPER_REG_BANK_CTRL
        STY MAPPER_REG_BANK_DATA ;0800-0FFF
        INY
VRAM_Init.Mapping_Vram_1000_1FFF;设置PPU地址 $1000-$1FFF, MMC3 8000写入$02,$03,$04,$05
        INX
        INY
        STX MAPPER_REG_BANK_CTRL
        STY MAPPER_REG_BANK_DATA ;1000-13FF, 1400-17FF, 1800-1BFF, 1C00-1FFF
        CPX #$05
        BCC VRAM_Init.Mapping_Vram_1000_1FFF
        
        ;设置数据起始地址
        LDA #<CHR_DATA_ADDR
        STA FC_Data_Addr_L
        LDA #>CHR_DATA_ADDR
        STA FC_Data_Addr_H
        
        ;设置VRAM写入起始地址 $0000
        LDY #$00
        STY PPU_ADDRESS
        STY PPU_ADDRESS
        LDX #$20        ;写入$2000字节($0100 * $20 = 8KB)
VRAM_Init.Write_Data
        LDA (FC_Data_Addr_L),Y
        STA PPU_DATA
        INY
        BNE VRAM_Init.Write_Data
        INC FC_Data_Addr_H
        DEX
        BNE VRAM_Init.Write_Data
        INC FC_Data_Temp   ;Bank号递增
        INC FC_Data_Count  ;计数器递增
        LDA FC_Data_Count  ;检查写入CHR Bank 数量
        CMP #NES_8KB_CHR_RAM_SIZE
        BCC VRAM_Init.Write_CHR_To_Vram
VRAM_Init.End
        PLA
        JSR Bank_Switch_A000
    .ENDIF
    RTS

;======================================================================
;节目选择处理
Item_Select_Process
    LDA FC_Nmi_Task_Flags
    AND #TASK_PAL_FADING_ENABLE | TASK_PAGING_ENABLE
    BNE Item_Select_Process.End
    LDA FC_Nmi_Task_Flags
    AND #TASK_ITEM_SELECT_ENABLE
    BEQ Item_Select_Process.End
    
    ;按了除了开始键之外的按键就禁用进入游戏准备状态
    LDA FC_Gamepad_Once
    AND #JOY_KEY_START ^ $FF
    BEQ Item_Select_Process.Pre_Item
    JSR Start_Ready_Disable
;前一项
Item_Select_Process.Pre_Item
    LDA FC_Gamepad_Once
    AND #JOY_KEY_UP
    BEQ Item_Select_Process.Next_Item
    JSR Game_Select_Pre_Item
    RTS
;下一项
Item_Select_Process.Next_Item
    LDA FC_Gamepad_Once
    AND #JOY_KEY_DOWN | JOY_KEY_SELECT
    BEQ Item_Select_Process.Pre_Page
    JSR Game_Select_Next_Item
    RTS
;前一页
Item_Select_Process.Pre_Page
    LDA FC_Gamepad_Once
    AND #JOY_KEY_LEFT
    BEQ Item_Select_Process.Next_Page
    JSR Game_Select_Pre_Page
    RTS
;下一页
Item_Select_Process.Next_Page
    LDA FC_Gamepad_Once
    AND #JOY_KEY_RIGHT
    BEQ Item_Select_Process.Enter_Item
    JSR Game_Select_Next_Page
    RTS
;进入节目
Item_Select_Process.Enter_Item
    LDA FC_Gamepad_Keep
    CMP FC_Gamepad_Once
    BNE Item_Select_Process.End
    
    LDA FC_Gamepad_Once
    BEQ Item_Select_Process.Enter
    CMP #JOY_KEY_START
    BNE Item_Select_Process.End
    
    ;启用进入游戏准备
    JSR Start_Ready_Enable
    RTS

;进入节目
Item_Select_Process.Enter
    LDA FC_Nmi_Task_Flags
    AND #TASK_START_READY
    BEQ Item_Select_Process.End
    JMP Enter_Game_Process
    JSR Start_Ready_Disable
Item_Select_Process.End
    RTS

;======================================================================
;进入游戏节目处理
Enter_Game_Process
    ;LDA #GAME_ENTER_TIME_DELAY
    ;JSR Wait_For_Time_Delay
    ;禁用节目选择
    JSR Select_Disable
    
    JSR Sound_For_Enter
    
    ;等待精灵调色板为0号索引, 避免渐出效果太突然
Enter_Game_Process.Wait
    LDA FC_Sp_Pal_Index
    BNE Enter_Game_Process.Wait
    
    LDA #MENU_OUT_TIME
    JSR Wait_For_Time_Delay

    JSR Pal_Update_Disable
    JSR Fade_Out_Process

    ;关闭屏幕
    LDA #$00
    STA PPU_CTRL
    STA PPU_MASK
    
    ;关闭音效
    STA APU_STATUS
    
    .IF 0 == MULTICART_TYPE
        JMP ($FFFC)
    .ELSE
        JMP Select_Game
    .ENDIF
Enter_Game_Process.End

;======================================================================
;等待VBlank到来
Wait_For_VBlank
    LDA PPU_STATUS
    BPL Wait_For_VBlank
    RTS

;======================================================================
;等待指定等待的时长结束
Wait_For_Time_Delay
    STA FC_Wait_Time_Delay
.Wait_Time_Out
    LDA FC_Wait_Time_Delay
    BNE .Wait_Time_Out
    RTS
    
;======================================================================
;启用节目选择
Select_Enable
    LDA FC_Nmi_Task_Flags
    ORA #TASK_ITEM_SELECT_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;禁用节目选择
Select_Disable
    LDA FC_Nmi_Task_Flags
    AND #$FF ^ TASK_ITEM_SELECT_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;启用调色板更新
Pal_Update_Enable
    LDA FC_Nmi_Task_Flags
    ORA #TASK_PAL_UPDATE_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;禁用调色板更新
Pal_Update_Disable
    LDA FC_Nmi_Task_Flags
    AND #$FF ^ TASK_PAL_UPDATE_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;设置渐变状态
Pal_Fade_Enable
    LDA FC_Nmi_Task_Flags
    ORA #TASK_PAL_FADING_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;清除渐变状态
Pal_Fade_Disable
    LDA FC_Nmi_Task_Flags
    AND #$FF ^ TASK_PAL_FADING_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;启用光标显示
Cursor_Enable
    LDA FC_Nmi_Task_Flags
    ORA #TASK_CURSOR_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;禁用光标显示
Cursor_Disable
    LDA FC_Nmi_Task_Flags
    AND #$FF ^ TASK_CURSOR_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;启用页面更新中
Paging_Enable
    LDA FC_Nmi_Task_Flags
    ORA #TASK_PAGING_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;禁用页面更新中
Paging_Disable
    LDA FC_Nmi_Task_Flags
    AND #$FF ^ TASK_PAGING_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;启用进入游戏就绪状态
Start_Ready_Enable
    LDA FC_Nmi_Task_Flags
    ORA #TASK_START_READY
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;禁用进入游戏就绪状态
Start_Ready_Disable
    LDA FC_Nmi_Task_Flags
    AND #$FF ^ TASK_START_READY
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;启用CHR更新
Chr_Update_Enable
    LDA FC_Nmi_Task_Flags
    ORA #TASK_CHR_UPDATE_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;禁用CHR更新
Chr_Update_Disable
    LDA FC_Nmi_Task_Flags
    AND #$FF ^ TASK_CHR_UPDATE_ENABLE
    STA FC_Nmi_Task_Flags
    RTS

;======================================================================
;重启处理
Reset_Program
    SEI
    CLD
    LDA #$00
    STA PPU_CTRL
    STA PPU_MASK
    STA JOY2_FRAME
    STA APU_STATUS
    
    ;等待屏幕准备完毕
    LDX #$02
Reset_Program.Wait_For_Screen_Ready
    LDA PPU_STATUS
    BPL Reset_Program.Wait_For_Screen_Ready
    DEX
    BNE Reset_Program.Wait_For_Screen_Ready
    
    ;清除声音 $4000-4013
    LDY #$14
    LDX #$00
    LDA #$00
Reset_Program.Sound_Clear
    STA $4000,X
    INX
    DEY
    BNE Reset_Program.Sound_Clear
    
    ;清除 RAM $0000-07FF
    LDA #$00
    STA $00
    STA $01
    TAY
    LDX #$08
Reset_Program.Memory_Clear
    STA ($00),Y
    INY
    BNE Reset_Program.Memory_Clear
    INC $01
    DEX
    BNE Reset_Program.Memory_Clear
    
    .IF 0 != MULTICART_TYPE
        JSR Menu_Size_Init
    .ENDIF
    
    ;精灵缓冲初始化
    LDX #$00
    LDA #$F8
Reset_Program.OAM_Clear
    STA OAM_DMA_Buffer,X
    INX
    BNE Reset_Program.OAM_Clear
    
    ;栈指针初始化
    LDX #$FF
    TXS
    
    ;首先清空调色板, 防止出现色条撕裂
    JSR Wait_For_VBlank
    JSR Palette_Clear;清空调色板
    
    JSR Mapper_Init;映射号初始化
    JSR Switch_Main_Bank;切换主程序需要的数据Bank
    
    JSR Nametable_Init;初始化命名表
    JSR Attributes_Init;初始化属性表
    JSR VRAM_Init;PPU图案内存初始化
    
    JSR Palette_Init;初始化调色板
    JSR Static_Text_Init;初始化静态文本
    JSR Chr_Update_Init;CHR缓冲初始化
    JSR Pal_Fade_Enable;标记渐入
    
    ;启用NMI处理, 设置背景图案表和精灵图案表
    LDA #$80 | ( ((BACKGROUND_PATTERN_TABLE&1) << 4) | ((SPRITE_PATTERN_TABLE&1) << 3))
    STA PPU_CTRL
    
    ;初始化背景调色板更新间隔
    LDA #BKG_PAL_UPDATE_TIME
    STA FC_Bkg_Pal_Time
    
    ;初始化精灵调色板更新间隔
    LDA #SP_PAL_UPDATE_TIME
    STA FC_Sp_Pal_Time
    
    ;初始化CHR更新间隔
    LDA #CHR_UPDATE_TIME
    STA FC_Chr_Time
    
    JSR Page_Text_Update;更新页面内容
    
    ;进入菜单延迟显示
    LDA #MENU_IN_TIME
    JSR Wait_For_Time_Delay
    
    ;设置屏幕显示状态缓冲(当NMI处理时控制屏幕显示状态)
    LDA #$1E
    STA FC_PPU_Mask_Buf
    
    ;菜单渐入处理
    JSR Fade_In_Process
    
    ;延迟播放背景音乐
    LDA #BGM_PLAY_TIME_DELAY
    JSR Wait_For_Time_Delay
    
    ;播放自定义背景音乐
    .IF CUSTOM_MUSIC_ENABLE
        .IF MUSIC_BGM > 0
            LDA #MUSIC_BGM - 1
            JSR Music_Init_Process
        .ENDIF
    .ENDIF
    
    JSR Pal_Update_Enable;启用调色板更新
    JSR Chr_Update_Enable;启用CHR更新
    JSR Pal_Fade_Disable;取消渐入标记
    
    ;启用节目选择
    JSR Select_Enable
    
    ;程序循环, 剩余工作交给 NMI 中断处理
Reset_Program.Main_Loop
    JMP Reset_Program.Main_Loop

;======================================================================
;不可屏蔽中断处理
Nmi_Program
    PHA
    TXA
    PHA
    TYA
    PHA
    
    LDA PPU_STATUS;读取使得 PPU状态寄存器复位
    
    JSR FC_PPU_Process;PPU处理
    
    ;精灵内存更新
    LDA #$00
    STA PPU_OAM_ADDR
    LDA #OAM_DMA_Buffer / $0100
    STA OAM_DMA
    
    ;重置节目文本待处理任务数
    LDA #$00
    STA FC_PPU_Item_Count

    ;备份Bank
Nmi_Program.Bank_Backup
    LDA FC_Bank_Bak_8000
    PHA
    LDA FC_Bank_Bak_A000
    PHA
    
    JSR FC_Cursor_Process;光标箭头处理
    JSR Bkg_Palette_Update;背景调色板更新处理
    JSR Sp_Palette_Update;精灵调色板更新处理
    JSR Chr_Update;CHR更新
    JSR FC_Gamepad_Process;手柄输入处理
    JSR FC_Gamepad_Turbo_Process;手柄长按连发处理
    JSR Item_Select_Process;节目选择处理
    
    JSR Chr_Bank_Update
    
    ;倒计时处理
    LDA FC_Wait_Time_Delay
    BEQ Nmi_Program.Music_Play
    DEC FC_Wait_Time_Delay
    
Nmi_Program.Music_Play

    JSR Music_Play_Process;音乐播放处理
    
    ;恢复Bank
    PLA 
    JSR Bank_Switch_A000
    PLA 
    JSR Bank_Switch_8000

Nmi_Program.End

    PLA
    TAY
    PLA
    TAX
    PLA
    RTI

;======================================================================
;请求中断处理
Irq_Program
    RTI

;======================================================================
;切页处理(将8KB bank 切到$8000-9FFF)
Chr_Bank_Update
    LDX #$00
    LDY #$00
.Write
    STX $8000
    LDA FC_CHR_Buf_Addr,Y
    STA $8001
    INY
    INX
    CPX #$06
    BCC .Write
    RTS

;======================================================================
;Mapper初始化
Mapper_Init
    MACRO_MAPPER_INIT
    RTS

;======================================================================
;音乐初始化处理
Music_Init_Process
    PHA
    JSR Switch_Music_Bank
    LDA #$1F
    STA APU_STATUS
    PLA
    JSR Music_Init_Addr
    JSR Switch_Main_Bank
    RTS

;======================================================================
;音乐播放处理
Music_Play_Process
    .IF CUSTOM_MUSIC_ENABLE
        JSR Switch_Music_Bank
        JSR Music_Play_Addr
        JSR Switch_Main_Bank
    .ENDIF
    RTS

;======================================================================
;切页处理(将8KB bank 切到$8000-9FFF)
Bank_Switch_8000
    STA FC_Bank_Bak_8000
    PHA
    MACRO_SWITCH_BANK_8000_MODE
    PLA
    MACRO_SWITCH_BANK_DATA
    RTS
;======================================================================
;切页处理(将8KB bank 切到$A000-BFFF)
Bank_Switch_A000
    STA FC_Bank_Bak_A000
    PHA
    MACRO_SWITCH_BANK_A000_MODE
    PLA
    MACRO_SWITCH_BANK_DATA
    RTS

;======================================================================
;切换音乐数据bank
Switch_Music_Bank
    LDA #MUSIN_DATA_BANK & BANK_DATA_MASK
    JSR Bank_Switch_8000
    LDA #(MUSIN_DATA_BANK + 1) & BANK_DATA_MASK
    JSR Bank_Switch_A000
    RTS

;======================================================================
;切换菜单数据bank
Switch_Main_Bank
    LDA #MENU_DATA_BANK & BANK_DATA_MASK
    JSR Bank_Switch_8000
    RTS

    .IF 1 == MULTICART_TYPE
        .INCLUDE "fc_game_coolboy.asm"
    .ENDIF
    
    .IF 2 == MULTICART_TYPE
        ;.INCLUDE "fc_game_coolgirl.asm"
    .ENDIF

;======================================================================
;中断向量表
    .ORG $FFFA
    .DW Nmi_Program
    .DW Reset_Program
    .DW Irq_Program